# -*- coding: utf-8 -*-
"""
Created on Mon Jan 30 11:28:59 2023

@author: huangyue
"""


'''
根据可转债的估值水平给出仓位建议
'''

from lib.GetDataJYDB import get_indexquote 
from lib.GetDataInfodb import get_existedinfo

import pymssql
                                 
from numpy import nan           
import pandas as pd

import datetime

d1 = datetime.timedelta(days=1)

# import matplotlib.pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
# plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

server_jydb = "10.10.0.102"
user_jydb = "jydb"
password_jydb = "jydb"

server_zs = "infodb"

variable_prefix = 'FICC|CBFitPremiumRateWithConvertValue'


# %% 函数
class CBtimeingStrategy:
    def get_enddate():
        '''
        大致获取最新一期的交易日
        '''
        enddate = (datetime.datetime.now() - d1*17.5/24)
        enddate = enddate+d1*(4 - (enddate.weekday() if enddate.weekday()>=5 else 4)) # 如果是周六周日则调整为周五
        return enddate

    def get_weighted_anadata(anadata, cmpwindow = 110, ma_window = 60,vol_para = 1,begdate = None,enddate = None,
                             get_latest_weight = False):
        '''
        根据传入的参数，计算当天的仓位
        '''   
        def get_weight(test,a,b):
            if test<0.5:
                return 1 - (test**a) / (0.5**a) * 0.5
            else:
                return 1 - (((test**b)-0.5**b) / (1 - 0.5**b) * 0.5 + 0.5)
        dfbt = anadata[['000832.CSI','value']].reset_index().rename(columns={'index':'date'})
        dfbt['ret'] = dfbt['000832.CSI'].pct_change() 
        
        # 计算分位数
        dfbt['quantile'] = dfbt['value'].rolling(cmpwindow).apply(lambda x: x.rank().values[cmpwindow-1]/cmpwindow).shift(1)
        # 根据分位数计算权重
        dfbt['weight'] = dfbt['quantile'].apply(lambda x:get_weight(x,vol_para,1/vol_para))
        
        # 权重移动平均
        dfbt['weight'] = dfbt['weight'].rolling(ma_window).mean()
        
        # 使用头一日的权重决定当日仓位
        latest_weight = dfbt['weight'].iloc[-1]
        dfbt['weight'] = dfbt['weight'].shift(1)
        
        # # 离散仓位
        # tmppara = 0.1
        # for i in range(0,11,2):
        #     tmpindex = dfbt[(dfbt['weight']>i/10-0.2) & (dfbt['weight']<i/10)].index
        #     dfbt.loc[tmpindex,'weight'] = i/10-tmppara
        
        # 调仓频率：周频
        dfbt['week'] = dfbt['date'].dt.week
        tmpindex = dfbt[dfbt['week'].diff()==0].index
        dfbt.loc[tmpindex,'weight'] = nan    
        dfbt['weight'] = dfbt['weight'].fillna(method='ffill')
        
        # 时间范围处理
        dfbt = dfbt[(dfbt['date']>=begdate)] if begdate is not None else dfbt
        dfbt = dfbt[(dfbt['date']<=enddate)] if enddate is not None else dfbt
        
        dfbt = dfbt.dropna().reset_index(drop=True)    # 删除第一段空值
        dfbt.loc[0,'ret'] = 0    # 第一个交易日的回报设为0
        
        
        dfbt['PNL'] = (dfbt['ret'] * dfbt['weight']+1).cumprod()    # 计算净值曲线
        raw_weight = 0.5    # 计算对比组净值曲线
        dfbt['raw_index'] = (dfbt['ret'] * raw_weight+1).cumprod()
        
        if get_latest_weight:
            return dfbt, latest_weight
        else:
            return dfbt
        
    def get_anadata(begdatestr, enddatestr):
        '''
        step1数据准备
        '''
        '''jydb的数据'''
        # 打开服务器
        conn_jydb = pymssql.connect(server_jydb, user_jydb, password_jydb, 'JYDB', charset='cp936')
        cursor_jydb = conn_jydb.cursor()
        # 中证转债指数
        DataZZZZ = get_indexquote(cursor_jydb, '000832.CSI', begdatestr, enddatestr)
        
        # 关闭服务器
        cursor_jydb.close()
        conn_jydb.close()
        
        
        '''infodb的数据'''
        # 打开服务器
        conn_zs = pymssql.connect(host=server_zs, database="DataCenter", charset='cp936')
        cursor_zs = conn_zs.cursor()
        # 历史上的模型拟合结果
        existedinfo = get_existedinfo(cursor_zs, variable_prefix+'_par', enddate = enddatestr,
                                      useenddate = True)   
        # 关闭服务器
        cursor_zs.close()
        conn_zs.close()
        
        '''# 数据准备'''
        his_model_all = existedinfo.drop(columns='SecuCode').pivot_table(values='Value',columns='Indicator',index = 'KeyDate')
        his_model_all.columns = list(tmpc[len(variable_prefix)+1:] for tmpc in his_model_all.columns)
        # hs300 = pd.merge(hs300,PE,on='date')
        
        # 提取平价110的转债估值，作为择时指标
        value_level = his_model_all['par_110'] 
        value_level.name='premiumRate'
        
        anadata = pd.merge(DataZZZZ.set_index('date'),value_level.to_frame(),left_index = True,right_index=True).sort_index()
        anadata['value'] = anadata['premiumRate']     
        return anadata
    
    @staticmethod
    def sensitivity_ana():
        '''
        敏感性分析
        '''
        # 数据提取
        enddate = CBtimeingStrategy.get_enddate()
        begdate = datetime.datetime(2017,1,1)        
        begdatestr = begdate.strftime('%Y-%m-%d')
        enddatestr = enddate.strftime('%Y-%m-%d')             
        anadata = CBtimeingStrategy.get_anadata(begdatestr, enddatestr)
        
        # 敏感性分析 
        RETinfo = pd.DataFrame()   # 用于保存敏感性分析表格        
        for cmpwindow in range(10,201,10): # 分位数窗口
            RETdict = {}
            for ma_window in range(10,121,10): # 平滑窗口   
                begdate_ana = enddate - 365*d1*3
                enddate_ana = enddate
                # 回测
                dfbt = CBtimeingStrategy.get_weighted_anadata(anadata, cmpwindow , ma_window, begdate = begdate_ana, enddate = enddate_ana)  
                
                # 计算策略超额
                tmpTimeRange = dfbt.shape[0]/250    # 时间范围
                tmpPNL = dfbt['PNL'].iloc[-1]**(1/tmpTimeRange) - dfbt['raw_index'].iloc[-1]**(1/tmpTimeRange)   # 年化超额收益率
                  
                RETdict[ma_window] = tmpPNL
                
            RETinfo = pd.concat([RETinfo,pd.DataFrame([RETdict],index=[cmpwindow])])
        
        # 格式调整
        # RETinfo = RETinfo.to_dict(orient='records')
        KeyDate = enddatestr
        return RETinfo,KeyDate
    
    @staticmethod
    def get_plot_data(**kwargs):
        # 数据提取
        enddate = CBtimeingStrategy.get_enddate()
        begdate = datetime.datetime(2017,1,1)        
        begdatestr = begdate.strftime('%Y-%m-%d')
        enddatestr = enddate.strftime('%Y-%m-%d')             
        anadata = CBtimeingStrategy.get_anadata(begdatestr, enddatestr)            
            
        cmpwindow = kwargs.get('cmpwindow', None)         # 回望参数
        ma_window = kwargs.get('ma_window', None)         # 平滑参数
        
        '''
        策略回测
        '''
        # 回测
        dfbt,latest_weight = \
            CBtimeingStrategy.get_weighted_anadata(anadata, cmpwindow ,
                                                   ma_window, get_latest_weight = True)  
        # 计算策略超额
        tmpTimeRange = dfbt.shape[0]/250    # 时间范围    
        dfbt['excess_PNL'] = dfbt['PNL']/dfbt['raw_index']
        
        dfbt['date'] = dfbt['date'].dt.strftime('%Y-%m-%d')
        ''' # 变量输出'''
        # （1）策略净值曲线，50%仓位中证转债净值曲线
        plot_data1 = dfbt[['date','PNL','raw_index']].to_dict(orient='records')
        # （2）最新仓位
        tmpdata = dfbt[['date','weight']]
        nextdate = enddate.date()+d1
        nextdatestr = nextdate.strftime('%Y-%m-%d')
        tmpdata = pd.concat([tmpdata,
            pd.DataFrame(data = [{'date':nextdatestr,'weight':latest_weight}])\
                ]).reset_index(drop=True)
        plot_data2 = tmpdata.to_dict(orient='records')
        # （3）超额收益
        plot_data3 = dfbt[['date','excess_PNL']].to_dict(orient='records')  
        # （4）年化超额收益率
        excess_ret = dfbt['PNL'].iloc[-1]**(1/tmpTimeRange) - dfbt['raw_index'].iloc[-1]**(1/tmpTimeRange)  
        # （5）最新仓位
        latest_holding = dfbt['weight'].iloc[-1]    
        
        return {'plot_data1':plot_data1,
                'plot_data2':plot_data2,
                'plot_data3':plot_data3,
                'excess_ret':excess_ret,
                'latest_holding':latest_holding}
    

        
 
    
























